package com.gitee.cirnochat.login.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.cirnochat.common.utils.IpInfoUtil;
import com.gitee.cirnochat.login.mapper.LoginDeviceMapper;
import com.gitee.cirnochat.login.model.dto.DeviceInfo;
import com.gitee.cirnochat.login.model.entity.IprangeInfo;
import com.gitee.cirnochat.login.model.entity.LoginDevice;
import com.gitee.cirnochat.login.model.vo.req.LoginDeviceUpdateRequest;
import com.gitee.cirnochat.login.model.vo.resp.LoginDeviceVo;
import com.gitee.cirnochat.login.service.IprangeInfoService;
import com.gitee.cirnochat.login.service.LoginDeviceService;
import com.gitee.cirnochat.notification.model.dto.NotificationDTO;
import com.gitee.cirnochat.notification.service.NotificationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Stream;

import static com.gitee.cirnochat.common.constant.RedisKey.ACCESS_TOKEN_KEY;
import static com.gitee.cirnochat.common.constant.RedisKey.REFRESH_TOKEN_KEY;


/**
 * @author FlanChan
 * @description 针对表【login_device(登陆设备)】的数据库操作Service实现
 * @createDate 2025-05-28 19:21:28
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LoginDeviceServiceImpl extends ServiceImpl<LoginDeviceMapper, LoginDevice>
        implements LoginDeviceService {

    private final IprangeInfoService iprangeInfoService;
    private final NotificationService notificationService;
    private final StringRedisTemplate redisTemplate;


    @Async
    @Override
    public void saveOrUpdateLoginDeviceAsync(String deviceId, String ipAddress, DeviceInfo deviceInfo, Long userId) {
        IprangeInfo iprangeInfo = iprangeInfoService.selectIpInfoByIpAddress(ipAddress);

        //先通过deviceId和userId查询是否存在记录
        LoginDevice existingDevice = this.lambdaQuery()
                .eq(LoginDevice::getDeviceId, deviceId)
                .eq(LoginDevice::getUserId, userId)
                .one();

        if (existingDevice != null) {
            // 如果存在记录，更新设备信息
            existingDevice.setDeviceName(deviceInfo.getDeviceName());
            existingDevice.setDeviceType(deviceInfo.getDeviceType());
            existingDevice.setIpAddress(ipAddress);
            existingDevice.setIpDescription(IpInfoUtil.getIpDescription(iprangeInfo));
            existingDevice.setUpdateTime(LocalDateTime.now());
            this.updateById(existingDevice);
            return;
        }
        LoginDevice loginDevice = new LoginDevice();
        loginDevice.setDeviceId(deviceId);
        loginDevice.setDeviceName(deviceInfo.getDeviceName());
        loginDevice.setDeviceType(deviceInfo.getDeviceType());
        loginDevice.setUserId(userId);
        loginDevice.setIpAddress(ipAddress);
        loginDevice.setIpDescription(IpInfoUtil.getIpDescription(iprangeInfo));
        this.save(loginDevice);
    }

    @Async
    @Override
    public void deleteLoginDeviceByDeviceIdAsync(String deviceId, Long userId) {
        //删除登陆设备
        this.remove(new LambdaQueryWrapper<>(LoginDevice.class)
                .eq(LoginDevice::getDeviceId, deviceId)
                .eq(LoginDevice::getUserId, userId));
        //推送下线通知
        notificationService.sendPushNotificationAsync(
                new NotificationDTO(
                        "下线通知",
                        "您的设备已下线"),
                        deviceId, userId);
        //移除redis中的ak , rk
        redisTemplate.delete(Stream.of(REFRESH_TOKEN_KEY,ACCESS_TOKEN_KEY).map(
                key -> key + String.format("%s#%s",deviceId,userId)).toList());
    }


    @Override
    public List<LoginDeviceVo> getLoginDevicesByUserId(Long userId) {
        return this.list(new LambdaQueryWrapper<>(LoginDevice.class)
                .eq(LoginDevice::getUserId, userId))
                .stream()
                .map(LoginDeviceVo::new)
                .toList();
    }

    @Override
    public void updateLoginDevice(LoginDeviceUpdateRequest req, Long userId) {
        // 更新登录设备信息
        this.update(new LambdaUpdateWrapper<>(LoginDevice.class)
                .eq(LoginDevice::getDeviceId, req.getDeviceId())
                .eq(LoginDevice::getUserId, userId)
                .set(req.getDeviceName() != null,LoginDevice::getDeviceName, req.getDeviceName()));
    }

    @Override
    public void deleteLoginDeviceByUserId(String deviceId, Long userId) {
        // 删除指定用户的登录设备
        this.remove(new LambdaQueryWrapper<>(LoginDevice.class)
                .eq(LoginDevice::getDeviceId, deviceId)
                .eq(LoginDevice::getUserId, userId));
        // 推送下线通知
        notificationService.sendPushNotificationAsync(
                new NotificationDTO(
                        "下线通知",
                        "您的设备已下线"),
                deviceId, userId);
        // 移除redis中的ak , rk
        redisTemplate.delete(Stream.of(REFRESH_TOKEN_KEY, ACCESS_TOKEN_KEY).map(
                key -> key + String.format("%s#%s", deviceId, userId)).toList());
    }

    @Override
    public void deleteBatchLoginDevice(List<String> deviceIds, Long userId) {
        // 批量删除指定用户的登录设备
        this.remove(new LambdaQueryWrapper<>(LoginDevice.class)
                .in(LoginDevice::getDeviceId, deviceIds)
                .eq(LoginDevice::getUserId, userId));
        // 推送下线通知
        deviceIds.forEach(deviceId -> {
            notificationService.sendPushNotificationAsync(
                    new NotificationDTO(
                            "下线通知",
                            "您的设备已下线"),
                    deviceId, userId);
            log.info("批量下线设备: {}", deviceId);
        });
        // 移除redis中的ak , rk
        deviceIds.forEach(deviceId -> redisTemplate.delete(Stream.of(REFRESH_TOKEN_KEY, ACCESS_TOKEN_KEY).map(
                key -> key + String.format("%s#%s", deviceId, userId)).toList()));
    }

    @Override
    public void deleteAllLoginDevicesExceptCurrentByUserId(String deviceId, Long userId) {
        List<LoginDevice> devices = this.list(new LambdaQueryWrapper<>(LoginDevice.class)
                .eq(LoginDevice::getUserId, userId)
                .ne(LoginDevice::getDeviceId, deviceId));
        // 删除除当前设备以外的所有登录设备
        this.remove(new LambdaQueryWrapper<>(LoginDevice.class)
                .eq(LoginDevice::getUserId, userId)
                .ne(LoginDevice::getDeviceId, deviceId));
        // 推送下线通知
        devices.forEach(device -> {
            // 移除redis中的ak , rk
            redisTemplate.delete(Stream.of(REFRESH_TOKEN_KEY, ACCESS_TOKEN_KEY).map(
                    key -> key + String.format("%s#%s", device.getDeviceId(), userId)).toList());
            notificationService.sendPushNotificationAsync(
                    new NotificationDTO(
                            "下线通知",
                            "您的设备已下线"),
                    deviceId, userId);
            log.info("下线设备: {}", device.getDeviceId());
        });
    }
}




